#pragma once
#include "spacepartitioner.h"
#include <vector>
#include "GLaddon/OpenGLFunc.h"


template <class Polyhedron, class Kernel>
class CMeshVoxelizer :
	public CSpacePartitioner<Polyhedron, Kernel>
{
	typedef	typename Kernel::FT			FT;
	typedef struct _VOXEL {
		unsigned short t;
		FT c[3];
	} VOXEL;
	typedef typename Polyhedron::Facet_iterator Facet_iterator;
	typedef	typename Kernel::Point_3	Point;
	typedef typename Polyhedron::Halfedge_around_facet_circulator HF_circulator;

public:
	CMeshVoxelizer(void) : CSpacePartitioner()
	{
		m_nDim = 0;
		voxels = NULL;
	}
	CMeshVoxelizer(Polyhedron *pMesh) : CSpacePartitioner(pMesh)
	{
		m_nDim = 0;
		m_nInVox = 0;
		voxels = NULL;
	}
	CMeshVoxelizer(Polyhedron *pMesh, int dim) : CSpacePartitioner(pMesh)
	{
		m_nDim = dim;
		m_nInVox = 0;
		voxels = NULL;
	}
	~CMeshVoxelizer(void)
	{
		deleteVoxels();
		m_nInVox = 0;
	}

public:
	void PartitionMesh(void)
	{
		if (m_pMesh == NULL) {
			AfxMessageBox("Mesh cannot be NULL!");
			return;
		}
		if (!m_pMesh->is_unitized()) {
			m_pMesh->unitize();
		}
		m_fRes = m_pMesh->get_unitizing_scale_factor() / m_nDim;
		FT cx = 0.5 * (m_fRes - m_pMesh->get_unitizing_scale_factor());
		voxels = new VOXEL**[m_nDim];
		m_nInVox = 0;
		for (int i=0; i<m_nDim; i++) {
			FT cy = 0.5 * (m_fRes - m_pMesh->get_unitizing_scale_factor());
			voxels[i] = new VOXEL*[m_nDim];
			for (int j=0; j<m_nDim; j++) {
				FT cz = 0.5 * (m_fRes - m_pMesh->get_unitizing_scale_factor());
				voxels[i][j] = new VOXEL [m_nDim];
				for (int k=0; k<m_nDim; k++) {
					voxels[i][j][k].c[0] = cx;
					voxels[i][j][k].c[1] = cy;
					voxels[i][j][k].c[2] = cz;
					if (isInsideModel(voxels[i][j][k].c)) { //only test if the center of the voxel is inside the mesh!
						voxels[i][j][k].t = 1;
						m_nInVox++;
					} else {
						voxels[i][j][k].t = 0;
					}
					cz += m_fRes;
				}
				cy += m_fRes;
			}
			cx += m_fRes;
		}
	}
/*
	void DrawSpatialPartition(void)
	{
		if (voxels == NULL) {
			return;
		}
		GLfloat green[] = {0.0f, 1.0f, 0.0f, 0.5f};
		GLfloat red[] = {1.0f, 0.0f, 0.0f, 0.5f};
		GLfloat blue[] = {0.0f, 0.0f, 1.0f, 0.5f};
		FT r = 0.5 * m_fRes;
		glPushAttrib(GL_LIGHTING_BIT|GL_ENABLE_BIT);
		glEnable(GL_BLEND);

		glBegin(GL_QUADS);
		for (int i=0; i<m_nDim; i++) {
			for (int j=0; j<m_nDim; j++) {
				for (int k=0; k<m_nDim; k++) {
					if (voxels[i][j][k].t == 1) {
						glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blue);
					} else if (voxels[i][j][k].t == 2) {
						glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
					} else if (voxels[i][j][k].t == 3) {
						glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
					} else {
						continue;
					}
					// Far
					glNormal3f(0.0f, 0.0f, -1.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					// Near
					glNormal3f(0.0f, 0.0f, 1.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					// Left
					glNormal3f(-1.0f, 0.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					// Right
					glNormal3f(1.0f, 0.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					// Bottom
					glNormal3f(0.0f, -1.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					// Top
					glNormal3f(0.0f, 1.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
				}
			}
		}
		glEnd();
		glPopAttrib();
	}
*/
	void DrawSpatialPartition(void)
	{
		if (voxels == NULL) {
			return;
		}
		FT r = 0.5 * m_fRes;
		glPushAttrib(GL_CURRENT_BIT|GL_ENABLE_BIT);
		glDisable(GL_LIGHTING);
		glColor3f(0.0f, 0.0f, 0.0f);
		for (int i=0; i<m_nDim; i++) {
			for (int j=0; j<m_nDim; j++) {
				for (int k=0; k<m_nDim; k++) {
					if (voxels[i][j][k].t == 0) {
						continue;
					}
					// Far
					glBegin(GL_LINE_LOOP);
					glNormal3f(0.0f, 0.0f, -1.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glEnd();
					// Near
					glBegin(GL_LINE_LOOP);
					glNormal3f(0.0f, 0.0f, 1.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glEnd();
					// Left
					glBegin(GL_LINE_LOOP);
					glNormal3f(-1.0f, 0.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glEnd();
					// Right
					glBegin(GL_LINE_LOOP);
					glNormal3f(1.0f, 0.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glEnd();
					// Bottom
					glBegin(GL_LINE_LOOP);
					glNormal3f(0.0f, -1.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]-r, voxels[i][j][k].c[2]-r);
					glEnd();
					// Top
					glBegin(GL_LINE_LOOP);
					glNormal3f(0.0f, 1.0f, 0.0f);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glVertex3F(voxels[i][j][k].c[0]+r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]-r);
					glVertex3F(voxels[i][j][k].c[0]-r, voxels[i][j][k].c[1]+r, voxels[i][j][k].c[2]+r);
					glEnd();
				}
			}
		}
		glEnd();
		glPopAttrib();
	}

	bool StoreProfile(const CString &sFileName)
	{
		return true;
	}
	bool LoadProfile(const CString &sFileName)
	{
		return true;
	}
	
private:
	void deleteVoxels(void)
	{
		for (int i=0; i<m_nDim; i++) {
			for (int j=0; j<m_nDim; j++) {
				delete [] voxels[i][j];
			}
			delete [] voxels[i];
		}
		delete [] voxels;
		voxels = NULL;
	}
public:
	VOXEL ***voxels;
	int m_nDim;
	FT m_fRes;
	int m_nInVox;	// number of voxels inside the mesh
};
